﻿using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using KCPProject;
using System.IO;

namespace Network 
{
    public class KcpConnection: IConnection
    {
        private bool isInit;
        private Socket mUdpClient;
        private IPEndPoint mSvrEndPoint;
        private EndPoint mIPEndPoint;
        private KCP mKcp;
        private bool mNeedUpdateFlag;
        private UInt32 mNextUpdateTime;
        private byte[] mRecvBuffer;
        private Queue<byte[]> mRecvQueue = new Queue<byte[]>(128);
        private Queue<MsgObject> mUnreliableRecvQueue = new Queue<MsgObject>(128);
        UInt32 CurrentTime;
        const UInt32 keepAliveInterval = 500;
        UInt32 LastSendTime;
        const UInt32 STABLE_CONN_RECV_INTERVAL = 1000;
        UInt32 mLastRecvTime;
        public bool IsStable
        {
            get
            {
                return CurrentTime - mLastRecvTime < STABLE_CONN_RECV_INTERVAL;
            }
        }


        static readonly public int msgTypeSize = 1;
        static readonly public int msgIdSize = 2;
        /// <summary>
        /// 是否转换大端小端
        /// </summary>
        public bool reverseByte = true;
        public void Connect(string host, UInt16 port, UInt32 conv)
        {
            CurrentTime = 0;
            IPAddress address = IPAddress.Parse(host);
            mSvrEndPoint = new IPEndPoint(address, port);
            mIPEndPoint = new IPEndPoint(address, port);
            mUdpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            mUdpClient.Blocking = false;
            init_kcp(conv);
            mRecvBuffer = new byte[mKcp.GetMtu()];
            mRecvQueue.Clear();
            mUnreliableRecvQueue.Clear();
            isInit = true;
        }

        void init_kcp(UInt32 conv)
        {
            mKcp = new KCP(conv, (byte[] buf, int size) =>
            {
                try
                {
                    LastSendTime = CurrentTime;
                    mUdpClient.SendTo(buf, size, SocketFlags.None, mSvrEndPoint);
                }
                catch(SocketException e)
                {
                }
            });

            // fast mode.
            mKcp.NoDelay(1, 10, 2, 1);
            mKcp.WndSize(128, 128);
        }

        public void Close()
        {
            mUdpClient = null;
            mKcp = null;
            isInit = false;
        }

        void process_recv_queue()
        {
            int len = 0;
            try
            {
                do
                {
                    len = mUdpClient.ReceiveFrom(mRecvBuffer, ref mIPEndPoint);
                    if (len > 0)
                    {
                        mLastRecvTime = CurrentTime;
                        uint conv = 0;
                        KCP.ikcp_decode32u(mRecvBuffer, 0, ref conv);
                        if (conv == 0) //normal udp
                        {
                            MemoryStream msgStream = new MemoryStream(mRecvBuffer);
                            BinaryReader reader = new BinaryReader(msgStream);
                            reader.ReadUInt32();
                            int size = len - sizeof(uint);
                            byte[] data = reader.ReadBytes(msgIdSize);
                            ushort msgID;
                            if (reverseByte)
                            {
                                msgID = NetUtilites.ByteReverse_ushort(data);
                            }
                            else
                            {
                                msgID = BitConverter.ToUInt16(data, 0);
                            }
                            MsgType msgType = (MsgType)reader.ReadBytes(msgTypeSize)[0];
                            data = reader.ReadBytes(size - msgIdSize - msgTypeSize);
                            mUnreliableRecvQueue.Enqueue(new MsgObject(msgID, data, msgType));
                        }
                        else //kcp
                        {
                            byte[] data = new byte[len];
                            Array.Copy(mRecvBuffer, data, len);
                            mRecvQueue.Enqueue(data);
                        }
                    }
                }
                while (len > 0);
            }
            catch(SocketException e)
            {

            }

            while (mRecvQueue.Count > 0)
            {
                var buf = mRecvQueue.Dequeue();

                mKcp.Input(buf);
                mNeedUpdateFlag = true;
            }
        }

        public void SendMessage(ushort id, MsgType type, byte[] f_buf)
        {
            if (isInit == false)
                return;

            int bufSize;
            if (f_buf == null)
            {
                bufSize = 0;
            }
            else
            {
                bufSize = f_buf.Length;
            }

            ushort uintID = id;
            if (reverseByte)
            {
                uintID = NetUtilites.ByteReverse_ushort(uintID);
            }

            byte[] data = new byte[bufSize + msgIdSize + msgTypeSize];

            BitConverter.GetBytes(uintID).CopyTo(data, 0);

            data[msgIdSize] = (byte)type;

            if (f_buf != null)
            {
                f_buf.CopyTo(data, msgIdSize + msgTypeSize);
            }
            mKcp.Send(data);
            mNeedUpdateFlag = true;
        }

        public MsgObject GetMsg()
        {
            if (isInit == false)
                return null;
            MsgObject msg = null;
            int size = mKcp.PeekSize();
            if (size > 0)
            {
                var buffer = new byte[size];
                MemoryStream msgStream = new MemoryStream(buffer);
                BinaryReader reader = new BinaryReader(msgStream);
                byte[] data = null;
                if (mKcp.Recv(buffer) > 0)
                {
                    data = reader.ReadBytes(msgIdSize);
                    ushort msgID;
                    if (reverseByte)
                    {
                        msgID = NetUtilites.ByteReverse_ushort(data);
                    }
                    else
                    {
                        msgID = BitConverter.ToUInt16(data, 0);
                    }
                    MsgType msgType = (MsgType)reader.ReadBytes(msgTypeSize)[0];
                    data = reader.ReadBytes(size - msgIdSize - msgTypeSize);
                    msg = new MsgObject(msgID, data, msgType);
                }
            } 
            else if(mUnreliableRecvQueue.Count > 0)
            {
                msg = mUnreliableRecvQueue.Dequeue();
            }
            return msg;
        }

        public void Tick(float deltaTime)
        {
            if (isInit == false)
                return;
            CurrentTime += (UInt32)(deltaTime * 1000);

            if (CurrentTime - LastSendTime > keepAliveInterval)
            {
                SendMessage(0, MsgType.KCP_KEEPALICE, null);
            }
                

            process_recv_queue();

            if (mNeedUpdateFlag || CurrentTime >= mNextUpdateTime)
            {
                mKcp.Update(CurrentTime);
                mNextUpdateTime = mKcp.Check(CurrentTime);
                mNeedUpdateFlag = false;
            }
        }
    }
}
